package com.wxxymaker.interceptor;

import com.blade.kit.Base64;
import com.wxxymaker.interceptor.annocation.AuthRequire;
import com.wxxymaker.utils.Const;
import com.wxxymaker.utils.auth.AuthModel;
import com.wxxymaker.utils.auth.AuthService;
import com.wxxymaker.utils.auth.AuthUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @Author: Soft
 * @Date: 2018/5/15 20:59
 * @Desc: 使用注解的用户身份验证
 */
public class AuthInterceptor extends HandlerInterceptorAdapter {
    private final static Logger log = LoggerFactory.getLogger(AuthInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HandlerMethod method = null;
        try {
            method = (HandlerMethod) handler;
        } catch (Exception e) {
            log.error("不是spring mvc所管理的处理器，无法进行转换-> {}", e.getMessage());
            return true;
        }

        AuthRequire authRequire = method.getMethodAnnotation(AuthRequire.class);

        if (authRequire != null) {
            HttpSession session = request.getSession();
            Object token = session.getAttribute("Auth-Token");
            return Const.AUTH.equals(token);
        }
        return true;
    }

    /**
     * 检测用户信息是否正确
     *
     * @param userInfo 加密过的用户信息
     * @param request  当前请求对象
     * @param service  用来从数据库获取用户信息的业务层实例
     * @param clazz    具体的实体类型
     * @param key      将数据库中获取到的实体信息放在request中的键
     * @return 是否验证成功
     */
    private boolean check(String userInfo, HttpServletRequest request, AuthService service,
                          Class<? extends AuthModel> clazz, String key) {
        AuthModel model = AuthUtil.fromString(userInfo, clazz);
        if (model != null) {
            AuthModel sqlModel = service.getByUsername(model.getUsername());
            if (null != sqlModel && sqlModel.getPassword().equals(model.getPassword())) {
                // 将用户信息放入Session中，便于跨请求获取用户信息，权宜
                request.getSession().setAttribute(key, sqlModel);
                request.setAttribute(key, sqlModel);
                return true;
            }
        }
        return false;
    }

    /**
     * 基准用户验证拦截方法
     *
     * @param request    当前请求对象
     * @param response   当前响应对象
     * @param sessionKey session键
     * @param cookieKey  cookie键
     * @param login      验证失败后添转的页面
     * @param service    用来从数据库获取用户信息的业务层实例
     * @param clazz      具体的实体类型
     * @param key        将数据库中获取到的实体信息放在request中的键
     * @return 成功与否，失败同时进行拦截跳转
     */
    protected boolean auth(HttpServletRequest request, HttpServletResponse response,
                           String sessionKey, String cookieKey, String login,
                           AuthService service, Class<? extends AuthModel> clazz, String key) throws IOException {
        HttpSession httpSession = request.getSession();
        Object session = httpSession.getAttribute(sessionKey);
        // 先验证session
        if (null != session) {
            if (this.check((String) session, request, service, clazz, key)) {
                httpSession.setAttribute("Auth-Token", Const.AUTH);
                return true;
            }
        } else { // 在验证cookie
            try {
                String userInfo = "";
                for (Cookie cookie : request.getCookies()) {
                    if (cookieKey.equals(cookie.getName())) {
                        userInfo = cookie.getValue();
                        break;
                    }
                }
                if (this.check(userInfo, request, service, clazz, key)) {
                    httpSession.setAttribute(sessionKey, userInfo);
                    httpSession.setAttribute("Auth-Token", Const.AUTH);
                    return true;
                }
            } catch (NullPointerException e) {
            }
        }

        String prevURL = request.getRequestURL().toString();
        String queryString = request.getQueryString();
        if (queryString != null) {
            prevURL += ("?" + queryString);
        }

        response.sendRedirect(login + "?prev=" +
                Base64.encodeToString(prevURL.getBytes(), Base64.NO_WRAP));
        return false;
    }
}
